package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    RestHighLevelClient client;

    /**
     * 根据传参查询
     * @param param
     * @return
     */
//    public PageResult list1(RequestParams param) {
//        try {
//            //获取请求参数
//            String key = param.getKey();
//            Integer page = param.getPage();
//            Integer size = param.getSize();
//            Integer curntPage = (page-1)*size;
//            Integer maxPrice = param.getMaxPrice();
//            Integer minPrice = param.getMinPrice();
//            String starName = param.getStarName();
//            String sortBy = param.getSortBy();
//
//            //创建请求对象
//            SearchRequest request = new SearchRequest("hotel");
//            //设置请求参数
//
//            if (!StringUtils.isAllBlank(key)){
//                request.source().query(QueryBuilders.matchQuery("all",key));
//             }else{
//                request.source().query(QueryBuilders.matchAllQuery());
//            }
//            //配置索引
//            request.source().from(curntPage).size(size);
//            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//
//            //处理响应请求
//            SearchHits hits = response.getHits();
//
//            //获取查询总条数
//            long value = hits.getTotalHits().value;
//
//            SearchHit[] DocArr = hits.getHits();
//
//            List<HotelDoc> list = new ArrayList<>();
//
//            if (DocArr.length!=0){
//
//                for (SearchHit doc : DocArr) {
//                    String json = doc.getSourceAsString();
//                    HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
//                    list.add(hotelDoc);
//                }
//            }
//            PageResult result = new PageResult();
//            result.setTotal(value);
//            result.setHotels(list);
//            return result;
//        } catch (IOException e) {
//            e.printStackTrace();
//            log.error("查询条件出错"+e.getMessage());
//        }
//        return null;
//    }

    @Override
    public PageResult list(RequestParams param) {

        try {
            //创建请求参数
            SearchRequest request = new SearchRequest("hotel");

            //设置分页
            request.source().from((param.getPage()-1)*param.getSize()).size(param.getSize());

            //创建复合查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

            //根据条件进行查询
            //传参是否有数据并设置高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            if (StringUtils.isNotBlank(param.getKey())){
                boolQuery.must(QueryBuilders.matchQuery("all",param.getKey()));
                highlightBuilder.field("name");
                highlightBuilder.preTags("<em>");
                highlightBuilder.postTags("</em>");
                highlightBuilder.requireFieldMatch(false);
                request.source().highlighter(highlightBuilder);

            }else{
                boolQuery.must(QueryBuilders.matchAllQuery());
            }
            //判断城市是否有数据
            if (StringUtils.isNotBlank(param.getCity())){
                boolQuery.filter(QueryBuilders.termQuery("city",param.getCity()));
            }
            //判断品牌是否有数据
            if (StringUtils.isNotBlank(param.getBrand())){
                boolQuery.filter(QueryBuilders.termQuery("brand",param.getBrand()));
            }
            //判断价格区间是否有数据
            if (param.getMaxPrice()!=null&&param.getMinPrice()!=null){
                boolQuery.filter(QueryBuilders.rangeQuery("price")
                        .gte(param.getMinPrice()).lte(param.getMaxPrice()));
            }
            //判断星级是否有数据
            if (StringUtils.isNotBlank(param.getStarName())){
                boolQuery.filter(QueryBuilders.termQuery("starName",param.getStarName()));
            }
            //判断地址是否有数据
            if (StringUtils.isNotBlank(param.getLocation())){
                request.source()
                        .sort(SortBuilders.geoDistanceSort("location", new GeoPoint(param.getLocation()))
                                .order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS)
                        );
            }



            //判断排序条件
            switch (param.getSortBy()){
                case("score"):
                    request.source().sort("score", SortOrder.DESC);
                    break;
                case ("price"):
                    request.source().sort("price", SortOrder.ASC);
                    break;
            }

            request.source().query(boolQuery);

            //构建算分函数
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                    boolQuery,
                    new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    QueryBuilders.termQuery("isAD",true),
                                    ScoreFunctionBuilders.weightFactorFunction(5)
                            )
                    }
            );

            request.source().query(functionScoreQueryBuilder);

            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            //处理响应请求
            SearchHits hits = response.getHits();

            //获取查询总条数
            long value = hits.getTotalHits().value;

            SearchHit[] DocArr = hits.getHits();



            List<HotelDoc> list = new ArrayList<>();

            if (DocArr.length!=0){

                for (SearchHit doc : DocArr) {
                    String json = doc.getSourceAsString();
                    HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
                    //获取高亮字段
                    Map<String, HighlightField> highlightFields = doc.getHighlightFields();

                    if (!CollectionUtils.isEmpty(highlightFields)){
                        HighlightField field = highlightFields.get("name");
                        if (field!= null){
                            System.err.println(field);
                            String newName = field.getFragments()[0].toString();
                            hotelDoc.setName(newName);
                        }
                    }
                    Object[] sortValues = doc.getSortValues();
                    if (sortValues.length>0){
                        hotelDoc.setDistance(sortValues[0]);
                    }

                    list.add(hotelDoc);
                }
            }

            PageResult result = new PageResult();
            result.setTotal(value);
            result.setHotels(list);
            return result;

        } catch (IOException e) {
            e.printStackTrace();
            log.error("查询时出错"+e.getMessage());
        }
        return null;
    }
}
